/**  
 * ls12.c  
 *   
 * 目的：列出指定目录或当前目录的内容，支持分栏显示、排序和 -a 选项  
 *   
 * 行为：  
 * - 如果没有参数，列出当前目录的内容  
 * - 否则，列出指定的目录的内容  
 *   
 * 选项：  
 * -a : 显示所有文件，包括隐藏文件  
 */  

#include <stdio.h>  
#include <stdlib.h>  
#include <sys/types.h>  
#include <dirent.h>  
#include <string.h>  
#include <ctype.h>  

/* 宏定义 */  
#define MAX_FILES 1024   // 支持的最大文件数  
#define MAX_FILENAME 256 // 单个文件名的最大长度  
#define COLUMN_WIDTH 20   // 每列的宽度  

/* 函数声明 */  
void list_directory(const char *dirname, int show_all);  
int compare(const void *a, const void *b);  
void print_columns(char files[][MAX_FILENAME], int count);  

int main(int argc, char *argv[]) {  
    int show_all = 0; // 默认不显示隐藏文件  
    int start = 1;    // 参数解析起始位置  

    /* 解析命令行选项 */  
    if (argc > 1 && argv[1][0] == '-') {  
        for (int i = 1; i < strlen(argv[1]); i++) {  
            if (argv[1][i] == 'a') {  
                show_all = 1;  
            } else {  
                fprintf(stderr, "不支持的选项: -%c\n", argv[1][i]);  
                exit(EXIT_FAILURE);  
            }  
        }  
        start = 2;  
    }  

    /* 如果没有参数或者只有选项参数，列出当前目录 */  
    if (argc == start) {  
        list_directory(".", show_all);  
    } else {  
        for (int i = start; i < argc; i++) {  
            printf("%s:\n", argv[i]);  
            list_directory(argv[i], show_all);  
            /* 多个目录时，分隔输出 */  
            if (i < argc - 1) {  
                printf("\n");  
            }  
        }  
    }  

    return 0;  
}  

/**  
 * 列出指定目录中的文件  
 *  
 * @param dirname 目录名称  
 * @param show_all 如果为1，则显示所有文件，包括隐藏文件  
 */  
void list_directory(const char *dirname, int show_all) {  
    DIR *dir_ptr = opendir(dirname);        // 打开目录  
    struct dirent *entry;  
    char files[MAX_FILES][MAX_FILENAME];    // 存储文件名  
    int count = 0;  

    if (dir_ptr == NULL) {  
        perror(dirname);  
        return;  
    }  

    /* 读取目录内容 */  
    while ((entry = readdir(dir_ptr)) != NULL) {  
        /* 如果不显示隐藏文件，跳过以 . 开头的文件 */  
        if (!show_all && entry->d_name[0] == '.') {  
            continue;  
        }  

        /* 检查是否超过最大文件数限制 */  
        if (count >= MAX_FILES) {  
            fprintf(stderr, "警告: 文件数超过限制 (%d)，部分文件可能未显示。\n", MAX_FILES);  
            break;  
        }  

        strncpy(files[count], entry->d_name, MAX_FILENAME - 1);  
        files[count][MAX_FILENAME - 1] = '\0'; // 确保字符串终止  
        count++;  
    }  

    /* 关闭目录 */  
    if (closedir(dir_ptr) != 0) {  
        perror("closedir");  
    }  

    /* 对文件名进行排序 */  
    qsort(files, count, MAX_FILENAME, compare);  

    /* 以多列格式打印文件名 */  
    print_columns(files, count);  
}  

/**  
 * 比较函数，用于 qsort 排序  
 *  
 * @param a 指向第一个字符串的指针  
 * @param b 指向第二个字符串的指针  
 * @return 比较结果  
 */  
int compare(const void *a, const void *b) {  
    const char *str1 = (const char *)a;  
    const char *str2 = (const char *)b;  
    return strcmp(str1, str2);  
}  

/**  
 * 以多列格式打印文件名  
 *  
 * @param files 存储文件名的二维数组  
 * @param count 文件数量  
 */  
void print_columns(char files[][MAX_FILENAME], int count) {  
    int cols, rows;  
    int i, j;  

    /* 计算终端宽度（假设为80字符） */  
    int term_width = 80;  
    cols = term_width / COLUMN_WIDTH;  
    if (cols == 0) cols = 1;  

    /* 计算需要的行数 */  
    rows = count / cols;  
    if (count % cols) rows++;  

    /* 打印分栏输出 */  
    for (i = 0; i < rows; i++) {  
        for (j = 0; j < cols; j++) {  
            int index = j * rows + i;  
            if (index < count) {  
                printf("%-*s", COLUMN_WIDTH, files[index]);  
            }  
        }  
        printf("\n");  
    }  
}
